package com.daoshu.mds.system.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.daoshu.component.exception.BusinessException;
import com.daoshu.component.jwt.JwtTokenFactory;
import com.daoshu.component.util.MD5Util;
import com.daoshu.component.util.MapUtil;
import com.daoshu.mds.constants.MessageType;
import com.daoshu.mds.dispatch.entity.MicroGroupMessageEntity;
import com.daoshu.mds.service.MessageFeignService;
import com.daoshu.mds.service.client.MessageClient;
import com.daoshu.mds.system.mapper.AccountMapper;
import com.daoshu.mds.system.service.*;
import com.daoshu.system.bo.AccountBo;
import com.daoshu.system.bo.OrganizationBo;
import com.daoshu.system.bo.PersonBo;
import com.daoshu.system.constants.ClientType;
import com.daoshu.system.entity.*;
import com.daoshu.system.utils.RequestUtils;
import io.jsonwebtoken.lang.Collections;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 *  账号服务 实现类
 * </p>
 *
 * @author zhaichen
 * @since 2019-09-03
 */
@Slf4j
@Service
public class AccountServiceImpl extends ServiceImpl<AccountMapper, AccountEntity> implements IAccountService {

	@Value("${Android.Key}")
	private String androidKey;
	@Value("${Android.Login.Count}")
	private int androidLoginCount;
	@Value("${Web.Key}")
	private String webKey;
	@Value("${Web.Login.Count}")
	private int webLoginCount;

	@Autowired
	IPersonService personService;

	@Autowired
	IAccountMenuService accountMenuService;

	@Autowired
	IAccountRoleService accountRoleService;

	@Autowired
	IMenuService menuService;

	@Autowired
	StringRedisTemplate redisTemplate;

	@Autowired
	IOrganizationService organizationService;

	@Autowired
	IOrganizationPersonService organizationPersonService;

	@Autowired
	RequestUtils requestUtils;

	@Autowired
	IPersonStatuService personStatuService;

	@Autowired
	MessageFeignService messageFeignService;

	@Autowired
	MessageClient messageClient;

	@Autowired
	IRoleService roleService;

	/**
	 * 验证账户信息，并且返回
	 *
	 * @param accountBo
	 */
	private void validateAccount(AccountBo accountBo) {
		if (null == accountBo) {
			throw new BusinessException(500, "请输入用户名、密码");
		}

		if (StringUtils.isBlank(accountBo.getLoginName())) {
			throw new BusinessException(500, "请输入用户名");
		}

		if (StringUtils.isBlank(accountBo.getPassword())) {
			throw new BusinessException(500, "请输入密码");
		}
		AccountEntity condition = new AccountEntity();
		condition.setLoginName(accountBo.getLoginName());
		condition.setPassword(MD5Util.MD5(accountBo.getPassword()));
		Wrapper<AccountEntity> queryWrapper = new QueryWrapper<AccountEntity>(condition);
		//AccountEntity accountEntity = Optional.ofNullable(this.getOne(queryWrapper)).orElseGet(()->validateAccount(accountBo.getLoginName(),accountBo.getPassword()));
		AccountEntity accountEntity = this.getOne(queryWrapper);
		if (null == accountEntity) {
			throw new BusinessException(500, "用户名和密码不匹配");
		}
		BeanUtils.copyProperties(accountEntity, accountBo);
	}

	/**
	 * 使用身份证号登陆
	 *
	 * @param idCard
	 * @param password
	 * @return
	 */
	private AccountEntity validateAccount(String idCard,String password) {
		if(StringUtils.isNotBlank(idCard) && StringUtils.isNotBlank(password)) {
			PersonEntity person = Optional.ofNullable(personService.getOne(new QueryWrapper<PersonEntity>().lambda()
					.eq(PersonEntity::getCredentialsNumber, idCard))).orElseThrow(()->{
				return new BusinessException(500,"账户不存在");
			});
			if(StringUtils.isNotBlank(person.getCredentialsNumber())) {
				AccountEntity account = Optional.ofNullable(this.getOne(new QueryWrapper<AccountEntity>().lambda()
						.eq(AccountEntity::getPersonId, person.getId()))).orElseThrow(()->{
					return new BusinessException(500,"账户不存在");
				});
				if(MD5Util.MD5(password).equals(account.getPassword())) {
					return account;
				}
			}
		}
		throw new BusinessException(500, "用户名和密码不匹配");
	}

	/**
	 * 获取账户登陆人信息
	 *
	 * @param accountBo
	 */
	private void getPersonInfo(AccountBo accountBo) {
		if (StringUtils.isNotBlank(accountBo.getPersonId())) {
			PersonEntity person = personService.getById(accountBo.getPersonId());
			if (null != person) {
				PersonBo personBo = new PersonBo();
				BeanUtils.copyProperties(person, personBo);
				accountBo.setPersonBo(personBo);
			}
		}
	}

	/**
	 * 获取权限菜单
	 *
	 * @param accountBo
	 */
	@SuppressWarnings("unused")
	private void getMenu(AccountBo accountBo) {
		//获取角色
		Set<String> menuIds = new HashSet<String>();
		menuIds.addAll(accountRoleService.getMenuIdsByAccountId(accountBo.getId()));
		menuIds.addAll(accountMenuService.getMenuIdsByAccountId(accountBo.getId()));
		if (!Collections.isEmpty(menuIds)) {
			accountBo.setMenus(new ArrayList<MenuEntity>(menuService.listByIds(menuIds)));
		}
	}

	/**
	 * 登陆信息存入redis中
	 *
	 * @param accountBo
	 */
	private void setRedis(AccountBo accountBo) {
		String key = new StringBuilder().append("*").append(accountBo.getLoginName()).append("-*").toString();
		Set<String> set = redisTemplate.keys(key);
		if ( null != set && set.size() > 0) {
			// 获取到当前登陆的用户，并踢出
			MicroGroupMessageEntity entity = new MicroGroupMessageEntity();
			for (String str : set) {
				AccountBo account = JSON.parseObject(redisTemplate.opsForValue().get(str), AccountBo.class);
				if(account!=null) {
					entity.setCreateId(account.getToken());
					// type=-1 代表下线通知
					entity.setType(MessageType.NOTICE_OFFLINE.getType());
					entity.setContent("您的账号在其他设备登录，请重新登录");
					redisTemplate.delete(set);
					try {
						messageClient.sendNoticeMessage(entity);
					} catch (Exception e) {
						e.printStackTrace();
						log.error("踢除token消息发送失败！"+e.getMessage());
					}
//					messageFeignService.sendNoticeMessage(entity);

					personStatuService.remove(new QueryWrapper<PersonStatusEntity>()
							.eq("person_id_", account.getPersonId()));
				}
			}
		}
		redisTemplate.opsForValue().set(getRedisKey(accountBo), JSON.toJSONString(accountBo), 24, TimeUnit.HOURS);
		redisTemplate.opsForHash().put("login_name_bo", accountBo.getLoginName(), JSON.toJSONString(accountBo));
	}

	private void setOrg(AccountBo accountBo) {
		List<OrganizationPersonEntity> entitys = organizationPersonService
				.list(new QueryWrapper<OrganizationPersonEntity>()
						.eq("person_id_", accountBo.getPersonId())
						.orderByDesc("main_position_")
						.orderByDesc("leader_"));
		if (CollectionUtils.isNotEmpty(entitys)) {
			OrganizationEntity orgEntity = organizationService.getById(entitys.get(0).getOrgId());
			if (null != orgEntity) {
				OrganizationBo organizationBo = new OrganizationBo();
				BeanUtils.copyProperties(orgEntity, organizationBo);
				accountBo.setOrganizationBo(organizationBo);
			}
		}
	}

	/**
	 * 获取用户角色信息
	 *
	 * @param accountBo 账户信息实体
	 */
	private void getRoleByAccountBo(AccountBo accountBo) {
		List<RoleEntity> roleEntityList = roleService.list(new QueryWrapper<>());

		AccountRoleEntity accountRoleEntity = new AccountRoleEntity();
		accountRoleEntity.setAccountId(accountBo.getId());
		QueryWrapper<AccountRoleEntity> accountRoleEntityParams = new QueryWrapper<>(accountRoleEntity);
		List<AccountRoleEntity> accountRoleEntityList = accountRoleService.list(accountRoleEntityParams);

		List<RoleEntity> roles = new ArrayList<>();
		for (RoleEntity role : roleEntityList) {
			for (AccountRoleEntity accountRole : accountRoleEntityList) {
				if (role.getId().equals(accountRole.getRoleId())) {
					roles.add(role);
				}
			}
		}
		accountBo.setRoleList(roles);
	}

	private void setAccountStatus(AccountBo accountbo) {
		ClientType clientType = requestUtils.geClientType();
		if (StringUtils.isNotBlank(accountbo.getPersonId())) {
			PersonStatusEntity status = personStatuService.getOne(new QueryWrapper<PersonStatusEntity>()
					.eq("person_id_", accountbo.getPersonId()));
			if (null == status) {
				PersonStatusEntity entity = new PersonStatusEntity();
				entity.setModifyTime(new Date());
				entity.setPersonId(accountbo.getPersonId());
				entity.setAccountId(accountbo.getId());
				entity.setClientType(clientType.getTitle());
				personStatuService.saveOrUpdate(entity);
			}else {
				status.setModifyTime(new Date());
				status.setAccountId(accountbo.getId());
				status.setClientType(clientType.getTitle());
				personStatuService.updateById(status);
			}
		}
	}

	@Override
	public AccountBo getAccount(AccountBo accountBo) {
		// step.2 获取登陆人员信息
		getPersonInfo(accountBo);
		// step.3 获取权限信息
//		getMenu(accountBo);

		// 获取用户角色
		getRoleByAccountBo(accountBo);

		setOrg(accountBo);

		return accountBo;
	}

	private void getLoginPermission(String clientType) {
		Set<String> set = redisTemplate.keys(clientType + "*");
		if (androidKey.equals(clientType) && set.size() > androidLoginCount) {
			throw new BusinessException(500, "Android端登陆不能超过" + androidLoginCount + "个");
		}
		if (webKey.equals(clientType) && set.size() > webLoginCount) {
			throw new BusinessException(500, "Web端登陆不能超过" + webLoginCount + "个");
		}
	}

	@Override
	public AccountBo login(AccountBo accountBo) {
		accountBo.setClientType(requestUtils.geClientType().getTitle());
		if (accountBo.getLoginName().equals("admin")) {
			if (!"123456".equals(accountBo.getPassword())) {
				throw new BusinessException(500, "用户名和密码不匹配");
			}
			accountBo = this.getAdmin();
		} else {
			// step.1 获取账户信息
			this.getLoginPermission(requestUtils.geClientType().getTitle());
			validateAccount(accountBo);
			this.getAccount(accountBo);
		}
		// step.4 生成token
		accountBo.setToken(JwtTokenFactory.createAccessJwtToken(accountBo.getLoginName(), "daoshu"));
		// step.5 存入redis
		setRedis(accountBo);
		setAccountStatus(accountBo);
		final AccountBo bo = accountBo;
		bo.setPassword("");
		return accountBo;
	}

	private AccountBo getAdmin() {
		AccountBo accountBo = new AccountBo();
		accountBo.setLoginName("admin");
		accountBo.setPersonBo(new PersonBo());
		accountBo.getPersonBo().setName("超级管理员");
		accountBo.setId("-1");
		accountBo.setCreatorId("0");
		accountBo.setPersonId("-1");
		accountBo.getPersonBo().setId("-1");
		accountBo.getPersonBo().setMainDuty("管理员");
		return accountBo;
	}

	private String getRedisKey(AccountBo accountBo) {
		StringBuffer stringBuffer = new StringBuffer();
		stringBuffer.append(accountBo.getClientType())
				.append("-")
				.append(accountBo.getLoginName())
				.append("-")
				.append(accountBo.getToken());
		log.info("redis key - " + stringBuffer.toString());
		return stringBuffer.toString();
	}

	@SuppressWarnings("unused")
	@Override
	public boolean logout() {
		AccountBo accountBo = requestUtils.getCurrentUser();
		final AccountBo bo = accountBo;
		return this.logout(accountBo);
	}

	@Override
	public boolean logout(AccountBo accountBo) {
		if(null == accountBo) {
			return false;
		}
		redisTemplate.opsForValue().getOperations().delete(accountBo.getToken());
		personStatuService.remove(new QueryWrapper<PersonStatusEntity>().lambda()
				.eq(PersonStatusEntity::getPersonId, accountBo.getPersonId()));
		return true;
	}
	@Override
	public boolean saveOrUpdateBo(AccountBo accountBo) {
		AccountEntity accountEntity = new AccountEntity();
		BeanUtils.copyProperties(accountBo, accountEntity);
		if (StringUtils.isNotBlank(accountBo.getId())) {
			accountMenuService.remove(new QueryWrapper<AccountMenuEntity>().eq("account_id_", accountBo.getId()));
			accountRoleService.remove(new QueryWrapper<AccountRoleEntity>().eq("account_id_", accountBo.getId()));
		}
		this.saveOrUpdate(accountEntity);

		List<String> menuIds = accountBo.getMenuIds();
		if (!Collections.isEmpty(accountBo.getMenuIds())) {
			AccountMenuEntity entity = new AccountMenuEntity();
			entity.setAccountId(accountEntity.getId());
			entity.setMenuIds(String.join(",", menuIds));
			accountMenuService.save(entity);
		}

		List<String> roleIds = accountBo.getRoleIds();
		if (!Collections.isEmpty(roleIds)) {
			Collection<AccountRoleEntity> list = new ArrayList<AccountRoleEntity>();
			AccountRoleEntity entity = null;
			for (String roleId : roleIds) {
				entity = new AccountRoleEntity();
				entity.setRoleId(roleId);
				entity.setAccountId(accountEntity.getId());
				list.add(entity);
			}
			accountRoleService.saveOrUpdateBatch(list);
		}
		return true;
	}

	@Override
	public AccountBo getByPersonId(String personId) {
		AccountBo bo = new AccountBo();
		if(StringUtils.isBlank(personId)) {
			return bo;
		}
		AccountEntity accountBo = Optional.ofNullable(this.getOne(new QueryWrapper<AccountEntity>().eq("person_id_", personId))).orElse(new AccountEntity());
		BeanUtils.copyProperties(accountBo, bo);
		bo.setPersonId(personId);
		getPersonInfo(bo);
		setOrg(bo);
		return bo;
	}

	@Override
	public AccountBo getPersonByPersonId(String personId) {
		if (org.apache.commons.lang3.StringUtils.isNotEmpty(personId)) {
			AccountBo accountBo = getByPersonId(personId);
			return getAccount(accountBo);
		}

		return null;
	}

	@Override
	public List<AccountBo> getPersonsByAccount(String[] accounts) {
		List<String> accountIds = new ArrayList<>();
		List<AccountBo> accountBos = new ArrayList<>();
		if (accountIds.size()>0) {
			listByIds(accountIds).stream().filter(item -> {
				if (StringUtils.isNotEmpty(item.getPersonId())) {
					AccountBo accountBo = MapUtil.map(item, AccountBo.class);
					PersonEntity person = personService.getById(item.getPersonId());
					if(null!=person) {
						accountBo.setPersonBo(MapUtil.map(person, PersonBo.class));
					}
					accountBos.add(accountBo);
					return true;
				} else {
					return false;
				}
			}).collect(Collectors.toList());
			return accountBos;
		}
		return null;
	}

	@Override
	public AccountBo getPersonsByAccount(String account) {
		List<AccountBo> list = getPersonsByAccount(new String[]{account});
		if (null != list && list.size() > 0) {
			return list.get(0);
		}

		return null;
	}

    /**
     * @brief update password
     * @param accountBo
     * @return
     */
	@Override
	public Boolean updatePassword(final AccountBo accountBo) {
	    // 1. 根据账号查询出账号的详细信息
        AccountEntity queryAE = this.getById(accountBo.getId());
        if (null == queryAE) {
            log.error("get account info failed");
            throw new BusinessException(501, "获取用户信息失败");
        }

        // 2. 判断当前密码和数据库存储的密码是否匹配，不匹配返回提醒
        if (!MD5Util.MD5(accountBo.getPassword()).equals(queryAE.getPassword())) {
            log.error("current password is validated");
            throw new BusinessException(501, "原密码输入错误");
        }

        // 3. 更新密码
        AccountEntity updateAE = new AccountEntity();
        updateAE.setId(accountBo.getId());
        updateAE.setPassword(MD5Util.MD5(accountBo.getNewPassword()));
        return this.updateById(updateAE);
	}
}
